import re

class CommandParser():
    def __init__(self, keyword=[], arg=[], leader=['-', '/'], content='[A-Za-z]+', argvalue=True, strict=False):
        """
        Content 
            example character      codech    run               -f                file       test.cc   
            corresponding content  [name]  [keyword]  [leader][arg](anywhere)  [argvalue]  [content]
        Argument instruction
            keyword    optional      list     the characters which mean the function of command(only one in a command)
            arg        optional    list/dict  the arguments(without leader, can be more than one); 
                                              dict >> {keyword: [corresponding args], };
                                              to express full named arg with symbol '--', 
                                              the value should be like ['-a', '-a-b'](must use leader '-')
            leader     optional      list     the leader symbol of the args
            content    optional    string:re  the rule to match the main content(or it may be matched as arg value)
            argvalue   optional      bool     whether allow having arg values
            strict     optional      bool     whether use strict mode
        Example
            parseCommand(keyword=['run', 'debug', 'compile'], arg=['c', 'f', '-file', '-code'],     
            leader=['-'], content='\w+\.cc|"[\w\W]+"', argvalue=True)
        """
        self.define(keyword, arg, leader, content, argvalue, strict)

    def define(self, keyword=[], arg=[], leader=['-', '/'], content='[A-Za-z]+', argvalue=True, strict=False):
        self.keyword = keyword 
        self.arg = arg
        self.leader = leader
        self.content = content
        self.argvalue = argvalue
        self.strict = strict

    def parse(self, command):
        """
        Argument instruction
            command   required   str/list   command string or sys.argv
        Return 
            dict >> {'keyword': ..., 'arg': ..., 'content': ...}
        """
        comlist = re.split('\s+', command) if isinstance(command, str) else command
        keyword = ''
        arg = {}
        last = ('', '')
        content = ''
        for i in range(len(comlist)):
            value = comlist[i]
            if not keyword and value in self.keyword:
                keyword = value
                last = ('keyword', value)
            elif re.match(str(self.leader).replace(',', '|').replace('\'', '').replace(' ', '') + '\S+', value):
                arg[value[1:]] = ''
                last = ('arg', value[1:])
            else:
                if last[0] == 'arg' and self.argvalue:
                    arg[last[1]] = value
                    last = ('argvalue', value)
                elif re.match(self.content, value):
                    content = value
                    last = ('content', value)
        new = {}
        if isinstance(self.arg, dict):
            if keyword:
                for a in arg.keys():
                    if a not in self.arg[keyword]:
                        if self.strict:
                            raise ValueError(f'invalid argument "{a}"')
                    else:
                        new[a] = arg[a]
        else:
            for a in arg.keys():
                if a not in self.arg:
                    if self.strict:
                        raise ValueError(f'invalid argument "{a}"')
                else:
                    new[a] = arg[a]
        return {'keyword': keyword, 'arg': new, 'content': content}
